package com.server.commons.security.Aspect;

import com.server.common.code.error.BusinessException;
import com.server.common.code.utils.OptionalUtils;
import com.server.common.redis.constant.RedisKeysConstant;
import com.server.common.security.code.anntation.RequestFrequencyLimit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RequestFrequencyLimitAspect {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(com.server.common.security.code.anntation.RequestFrequencyLimit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        RequestFrequencyLimit requestFrequencyLimit = method.getAnnotation(RequestFrequencyLimit.class);

        String requestMappingValue = method.getDeclaringClass().getAnnotation(RequestMapping.class).path()[0]
                                     + ":" + method.getName()
                ;

        String key = requestMappingValue + ":" + requestFrequencyLimit.condition().getName();

        String limitKey = RedisKeysConstant.REQUEST_FREQUENCY_LIMIT + key;
        String lockKey = RedisKeysConstant.REQUEST_FREQUENCY_LOCK + key;

        Boolean lockHasKey = redisTemplate.hasKey(lockKey);
        Boolean limitHasKey = redisTemplate.hasKey(limitKey);

        //判断是否被锁定
        if(Boolean.TRUE.equals(lockHasKey)){
            throw new BusinessException("请求次数频繁，稍后再试");
        }

        Integer limitNum = (Integer) redisTemplate.opsForValue().get(limitKey);
        int num = OptionalUtils.ofEmpty(limitNum).orElse(0) + 1;

        //次数频繁
        if(num > requestFrequencyLimit.limitNum()){
            redisTemplate.opsForValue().set(lockKey,1,requestFrequencyLimit.lockTime(), TimeUnit.MILLISECONDS);
            throw new BusinessException("请求次数频繁，稍后再试");
        }

        //如果存在- 计数+1
        if(Boolean.TRUE.equals(limitHasKey)){
            redisTemplate.opsForValue().increment(limitKey);
        }
        //不存在添加
        if(Boolean.FALSE.equals(limitHasKey)){
            redisTemplate.opsForValue().set(limitKey,1,requestFrequencyLimit.frameTime(), TimeUnit.MILLISECONDS);
        }

        return point.proceed();
    }

}
